From 6a5acde22674a9fc38bb93331578b8fd06321064 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 18 Aug 2023 11:34:54 -0400 Subject: [PATCH] Make `Parameters` an optional field on `ExprLambda` (#6669) ## Summary If a lambda doesn't contain any parameters, or any parameter _tokens_ (like `*`), we can use `None` for the parameters. This feels like a better representation to me, since, e.g., what should the `TextRange` be for a non-existent set of parameters? It also allows us to remove several sites where we check if the `Parameters` is empty by seeing if it contains any arguments, so semantically, we're already trying to detect and model around this elsewhere. Changing this also fixes a number of issues with dangling comments in parameter-less lambdas, since those comments are now automatically marked as dangling on the lambda. (As-is, we were also doing something not-great whereby the lambda was responsible for formatting dangling comments on the parameters, which has been removed.) Closes https://github.com/astral-sh/ruff/issues/6646. Closes https://github.com/astral-sh/ruff/issues/6647. ## Test Plan `cargo test` --- crates/ruff/src/checkers/ast/mod.rs | 30 +++-- .../rules/function_uses_loop_variable.rs | 5 +- .../rules/loop_variable_overrides_iterator.rs | 25 ++-- .../rules/unnecessary_map.rs | 29 +++-- .../rules/reimplemented_list_builtin.rs | 7 +- .../rules/flake8_pytest_style/rules/patch.rs | 23 ++-- .../rules/unused_arguments.rs | 5 +- .../pycodestyle/rules/lambda_assignment.rs | 11 +- crates/ruff_python_ast/src/comparable.rs | 4 +- crates/ruff_python_ast/src/node.rs | 4 +- crates/ruff_python_ast/src/nodes.rs | 2 +- crates/ruff_python_ast/src/visitor.rs | 4 +- crates/ruff_python_codegen/src/generator.rs | 8 +- .../test/fixtures/ruff/expression/lambda.py | 27 ++++- .../src/comments/placement.rs | 18 ++- .../src/expression/expr_lambda.rs | 44 +++---- .../src/other/parameters.rs | 6 +- .../format@expression__lambda.py.snap | 55 ++++++++- crates/ruff_python_parser/src/python.lalrpop | 8 +- crates/ruff_python_parser/src/python.rs | 10 +- ..._function__tests__lambda_kw_only_args.snap | 68 +++++------ ...ts__lambda_kw_only_args_with_defaults.snap | 112 +++++++++--------- ...rser__function__tests__lambda_no_args.snap | 9 +- ...n__tests__lambda_pos_and_kw_only_args.snap | 102 ++++++++-------- ...ser__function__tests__lambda_pos_args.snap | 68 +++++------ ..._tests__lambda_pos_args_with_defaults.snap | 112 +++++++++--------- ...r__parser__tests__match_as_identifier.snap | 36 +++--- ...n_parser__parser__tests__parse_lambda.snap | 52 ++++---- ...__parser__tests__parse_lambda_no_args.snap | 9 +- ...er__parser__tests__type_as_identifier.snap | 36 +++--- 30 files changed, 517 insertions(+), 412 deletions(-) diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 4cb93819cb..df93c18b11 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -874,18 +874,20 @@ where }, ) => { // Visit the default arguments, but avoid the body, which will be deferred. - for ParameterWithDefault { - default, - parameter: _, - range: _, - } in parameters - .posonlyargs - .iter() - .chain(¶meters.args) - .chain(¶meters.kwonlyargs) - { - if let Some(expr) = &default { - self.visit_expr(expr); + if let Some(parameters) = parameters { + for ParameterWithDefault { + default, + parameter: _, + range: _, + } in parameters + .posonlyargs + .iter() + .chain(¶meters.args) + .chain(¶meters.kwonlyargs) + { + if let Some(expr) = &default { + self.visit_expr(expr); + } } } @@ -1834,7 +1836,9 @@ impl<'a> Checker<'a> { range: _, }) = expr { - self.visit_parameters(parameters); + if let Some(parameters) = parameters { + self.visit_parameters(parameters); + } self.visit_expr(body); } else { unreachable!("Expected Expr::Lambda"); diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs b/crates/ruff/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs index b0291c21b6..7cbb231fbd 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/function_uses_loop_variable.rs @@ -184,7 +184,10 @@ impl<'a> Visitor<'a> for SuspiciousVariablesVisitor<'a> { return false; } - if parameters.includes(&loaded.id) { + if parameters + .as_ref() + .is_some_and(|parameters| parameters.includes(&loaded.id)) + { return false; } diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/loop_variable_overrides_iterator.rs b/crates/ruff/src/rules/flake8_bugbear/rules/loop_variable_overrides_iterator.rs index bee095c0cd..45439b1141 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/loop_variable_overrides_iterator.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/loop_variable_overrides_iterator.rs @@ -76,17 +76,20 @@ where range: _, }) => { visitor::walk_expr(self, body); - for ParameterWithDefault { - parameter, - default: _, - range: _, - } in parameters - .posonlyargs - .iter() - .chain(¶meters.args) - .chain(¶meters.kwonlyargs) - { - self.names.remove(parameter.name.as_str()); + + if let Some(parameters) = parameters { + for ParameterWithDefault { + parameter, + default: _, + range: _, + } in parameters + .posonlyargs + .iter() + .chain(¶meters.args) + .chain(¶meters.kwonlyargs) + { + self.names.remove(parameter.name.as_str()); + } } } _ => visitor::walk_expr(self, expr), diff --git a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs index c914753be2..8675682925 100644 --- a/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs +++ b/crates/ruff/src/rules/flake8_comprehensions/rules/unnecessary_map.rs @@ -103,7 +103,10 @@ pub(crate) fn unnecessary_map( return; }; - if late_binding(parameters, body) { + if parameters + .as_ref() + .is_some_and(|parameters| late_binding(parameters, body)) + { return; } } @@ -134,7 +137,10 @@ pub(crate) fn unnecessary_map( return; }; - if late_binding(parameters, body) { + if parameters + .as_ref() + .is_some_and(|parameters| late_binding(parameters, body)) + { return; } } @@ -171,7 +177,10 @@ pub(crate) fn unnecessary_map( return; } - if late_binding(parameters, body) { + if parameters + .as_ref() + .is_some_and(|parameters| late_binding(parameters, body)) + { return; } } @@ -240,7 +249,7 @@ struct LateBindingVisitor<'a> { /// The arguments to the current lambda. parameters: &'a Parameters, /// The arguments to any lambdas within the current lambda body. - lambdas: Vec<&'a Parameters>, + lambdas: Vec>, /// Whether any names within the current lambda body are late-bound within nested lambdas. late_bound: bool, } @@ -261,7 +270,7 @@ impl<'a> Visitor<'a> for LateBindingVisitor<'a> { fn visit_expr(&mut self, expr: &'a Expr) { match expr { Expr::Lambda(ast::ExprLambda { parameters, .. }) => { - self.lambdas.push(parameters); + self.lambdas.push(parameters.as_deref()); visitor::walk_expr(self, expr); self.lambdas.pop(); } @@ -275,11 +284,11 @@ impl<'a> Visitor<'a> for LateBindingVisitor<'a> { // If the name is defined in the current lambda... if self.parameters.includes(id) { // And isn't overridden by any nested lambdas... - if !self - .lambdas - .iter() - .any(|parameters| parameters.includes(id)) - { + if !self.lambdas.iter().any(|parameters| { + parameters + .as_ref() + .is_some_and(|parameters| parameters.includes(id)) + }) { // Then it's late-bound. self.late_bound = true; } diff --git a/crates/ruff/src/rules/flake8_pie/rules/reimplemented_list_builtin.rs b/crates/ruff/src/rules/flake8_pie/rules/reimplemented_list_builtin.rs index 18dd2d6e76..2c5a8bae51 100644 --- a/crates/ruff/src/rules/flake8_pie/rules/reimplemented_list_builtin.rs +++ b/crates/ruff/src/rules/flake8_pie/rules/reimplemented_list_builtin.rs @@ -59,12 +59,7 @@ pub(crate) fn reimplemented_list_builtin(checker: &mut Checker, expr: &ExprLambd range: _, } = expr; - if parameters.args.is_empty() - && parameters.kwonlyargs.is_empty() - && parameters.posonlyargs.is_empty() - && parameters.vararg.is_none() - && parameters.kwarg.is_none() - { + if parameters.is_none() { if let Expr::List(ast::ExprList { elts, .. }) = body.as_ref() { if elts.is_empty() { let mut diagnostic = Diagnostic::new(ReimplementedListBuiltin, expr.range()); diff --git a/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs b/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs index cfd9e7d8d3..229c13a28b 100644 --- a/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs +++ b/crates/ruff/src/rules/flake8_pytest_style/rules/patch.rs @@ -89,18 +89,19 @@ fn check_patch_call(call: &ast::ExprCall, index: usize) -> Option { .find_argument("new", index)? .as_lambda_expr()?; - // Walk the lambda body. - let mut visitor = LambdaBodyVisitor { - parameters, - uses_args: false, - }; - visitor.visit_expr(body); - - if visitor.uses_args { - None - } else { - Some(Diagnostic::new(PytestPatchWithLambda, call.func.range())) + // Walk the lambda body. If the lambda uses the arguments, then it's valid. + if let Some(parameters) = parameters { + let mut visitor = LambdaBodyVisitor { + parameters, + uses_args: false, + }; + visitor.visit_expr(body); + if visitor.uses_args { + return None; + } } + + Some(Diagnostic::new(PytestPatchWithLambda, call.func.range())) } /// PT008 diff --git a/crates/ruff/src/rules/flake8_unused_arguments/rules/unused_arguments.rs b/crates/ruff/src/rules/flake8_unused_arguments/rules/unused_arguments.rs index f580183982..d2d22e336a 100644 --- a/crates/ruff/src/rules/flake8_unused_arguments/rules/unused_arguments.rs +++ b/crates/ruff/src/rules/flake8_unused_arguments/rules/unused_arguments.rs @@ -436,7 +436,10 @@ pub(crate) fn unused_arguments( } } } - ScopeKind::Lambda(ast::ExprLambda { parameters, .. }) => { + ScopeKind::Lambda(ast::ExprLambda { + parameters: Some(parameters), + .. + }) => { if checker.enabled(Argumentable::Lambda.rule_code()) { function( Argumentable::Lambda, diff --git a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs index 66b90caeab..2ad00759e5 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -91,7 +91,7 @@ pub(crate) fn lambda_assignment( let mut indented = String::new(); for (idx, line) in function( id, - parameters, + parameters.as_deref(), body, annotation, checker.semantic(), @@ -180,7 +180,7 @@ fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec, body: &Expr, annotation: Option<&Expr>, semantic: &SemanticModel, @@ -190,6 +190,9 @@ fn function( value: Some(Box::new(body.clone())), range: TextRange::default(), }); + let parameters = parameters + .cloned() + .unwrap_or_else(|| Parameters::empty(TextRange::default())); if let Some(annotation) = annotation { if let Some((arg_types, return_type)) = extract_types(annotation, semantic) { // A `lambda` expression can only have positional and positional-only @@ -228,7 +231,7 @@ fn function( parameters: Box::new(Parameters { posonlyargs: new_posonlyargs, args: new_args, - ..parameters.clone() + ..parameters }), body: vec![body], decorator_list: vec![], @@ -242,7 +245,7 @@ fn function( let func = Stmt::FunctionDef(ast::StmtFunctionDef { is_async: false, name: Identifier::new(name.to_string(), TextRange::default()), - parameters: Box::new(parameters.clone()), + parameters: Box::new(parameters), body: vec![body], decorator_list: vec![], returns: None, diff --git a/crates/ruff_python_ast/src/comparable.rs b/crates/ruff_python_ast/src/comparable.rs index a28e6d2190..6051ace1d4 100644 --- a/crates/ruff_python_ast/src/comparable.rs +++ b/crates/ruff_python_ast/src/comparable.rs @@ -542,7 +542,7 @@ pub struct ExprUnaryOp<'a> { #[derive(Debug, PartialEq, Eq, Hash)] pub struct ExprLambda<'a> { - parameters: ComparableParameters<'a>, + parameters: Option>, body: Box>, } @@ -773,7 +773,7 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> { body, range: _, }) => Self::Lambda(ExprLambda { - parameters: (parameters.as_ref()).into(), + parameters: parameters.as_ref().map(Into::into), body: body.into(), }), ast::Expr::IfExp(ast::ExprIfExp { diff --git a/crates/ruff_python_ast/src/node.rs b/crates/ruff_python_ast/src/node.rs index c6a7e7d675..b565f86c26 100644 --- a/crates/ruff_python_ast/src/node.rs +++ b/crates/ruff_python_ast/src/node.rs @@ -2038,7 +2038,9 @@ impl AstNode for ast::ExprLambda { range: _, } = self; - visitor.visit_parameters(parameters); + if let Some(parameters) = parameters { + visitor.visit_parameters(parameters); + } visitor.visit_expr(body); } } diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index af9d150f54..7e09a26089 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -632,7 +632,7 @@ impl From for Expr { #[derive(Clone, Debug, PartialEq)] pub struct ExprLambda { pub range: TextRange, - pub parameters: Box, + pub parameters: Option>, pub body: Box, } diff --git a/crates/ruff_python_ast/src/visitor.rs b/crates/ruff_python_ast/src/visitor.rs index db9b856eb6..be51dda066 100644 --- a/crates/ruff_python_ast/src/visitor.rs +++ b/crates/ruff_python_ast/src/visitor.rs @@ -354,7 +354,9 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) { body, range: _, }) => { - visitor.visit_parameters(parameters); + if let Some(parameters) = parameters { + visitor.visit_parameters(parameters); + } visitor.visit_expr(body); } Expr::IfExp(ast::ExprIfExp { diff --git a/crates/ruff_python_codegen/src/generator.rs b/crates/ruff_python_codegen/src/generator.rs index d20d8afa4c..4408ff9f05 100644 --- a/crates/ruff_python_codegen/src/generator.rs +++ b/crates/ruff_python_codegen/src/generator.rs @@ -878,9 +878,11 @@ impl<'a> Generator<'a> { range: _, }) => { group_if!(precedence::LAMBDA, { - let npos = parameters.args.len() + parameters.posonlyargs.len(); - self.p(if npos > 0 { "lambda " } else { "lambda" }); - self.unparse_parameters(parameters); + self.p("lambda"); + if let Some(parameters) = parameters { + self.p(" "); + self.unparse_parameters(parameters); + } self.p(": "); self.unparse_expr(body, precedence::LAMBDA); }); diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/lambda.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/lambda.py index 5234ce79a0..32a57bed4a 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/lambda.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/lambda.py @@ -56,12 +56,16 @@ lambda x: lambda y: lambda z: ( z) # Trailing # Trailing - a = ( lambda # Dangling : 1 ) +a = ( + lambda x # Dangling + , y: 1 +) + # Regression test: lambda empty arguments ranges were too long, leading to unstable # formatting (lambda:(# @@ -93,3 +97,24 @@ lambda a, *args, b, **kwds,: 0 lambda a, *, b, **kwds,: 0 lambda a, /: a lambda a, /, c: a + +# Dangling comments without parameters. +( + lambda + : # 3 + None +) + +( + lambda + # 3 + : None +) + +( + lambda # 1 + # 2 + : # 3 + # 4 + None # 5 +) diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index 9cccfb027e..ff686ba55a 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -162,9 +162,14 @@ fn handle_enclosed_comment<'a>( locator: &Locator, ) -> CommentPlacement<'a> { match comment.enclosing_node() { - AnyNodeRef::Parameters(arguments) => { - handle_parameters_separator_comment(comment, arguments, locator) - .or_else(|comment| handle_bracketed_end_of_line_comment(comment, locator)) + AnyNodeRef::Parameters(parameters) => { + handle_parameters_separator_comment(comment, parameters, locator).or_else(|comment| { + if are_parameters_parenthesized(parameters, locator.contents()) { + handle_bracketed_end_of_line_comment(comment, locator) + } else { + CommentPlacement::Default(comment) + } + }) } AnyNodeRef::Arguments(_) | AnyNodeRef::TypeParams(_) => { handle_bracketed_end_of_line_comment(comment, locator) @@ -1544,6 +1549,13 @@ fn is_first_statement_in_alternate_body(statement: AnyNodeRef, has_body: AnyNode } } +/// Returns `true` if the parameters are parenthesized (as in a function definition), or `false` if +/// not (as in a lambda). +fn are_parameters_parenthesized(parameters: &Parameters, contents: &str) -> bool { + // A lambda never has parentheses around its parameters, but a function definition always does. + contents[parameters.range()].starts_with('(') +} + /// Counts the number of empty lines in `contents`. fn max_empty_lines(contents: &str) -> u32 { let mut newlines = 0u32; diff --git a/crates/ruff_python_formatter/src/expression/expr_lambda.rs b/crates/ruff_python_formatter/src/expression/expr_lambda.rs index d663084f39..b7d7d39b60 100644 --- a/crates/ruff_python_formatter/src/expression/expr_lambda.rs +++ b/crates/ruff_python_formatter/src/expression/expr_lambda.rs @@ -1,13 +1,14 @@ -use crate::comments::{dangling_node_comments, SourceComment}; +use ruff_formatter::prelude::{space, text}; +use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_python_ast::node::AnyNodeRef; +use ruff_python_ast::ExprLambda; + +use crate::comments::{dangling_comments, SourceComment}; use crate::context::PyFormatContext; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::other::parameters::ParametersParentheses; use crate::AsFormat; use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{space, text}; -use ruff_formatter::{write, Buffer, FormatResult}; -use ruff_python_ast::node::AnyNodeRef; -use ruff_python_ast::ExprLambda; #[derive(Default)] pub struct FormatExprLambda; @@ -20,13 +21,12 @@ impl FormatNodeRule for FormatExprLambda { body, } = item; + let comments = f.context().comments().clone(); + let dangling = comments.dangling(item); + write!(f, [text("lambda")])?; - if !parameters.args.is_empty() - || !parameters.posonlyargs.is_empty() - || parameters.vararg.is_some() - || parameters.kwarg.is_some() - { + if let Some(parameters) = parameters { write!( f, [ @@ -38,21 +38,15 @@ impl FormatNodeRule for FormatExprLambda { )?; } - write!( - f, - [ - text(":"), - space(), - body.format(), - // It's possible for some `Arguments` of `lambda`s to be assigned dangling comments. - // - // a = ( - // lambda # Dangling - // : 1 - // ) - dangling_node_comments(parameters.as_ref()) - ] - ) + write!(f, [text(":")])?; + + if dangling.is_empty() { + write!(f, [space()])?; + } else { + write!(f, [dangling_comments(dangling)])?; + } + + write!(f, [body.format()]) } fn fmt_dangling_comments( diff --git a/crates/ruff_python_formatter/src/other/parameters.rs b/crates/ruff_python_formatter/src/other/parameters.rs index 45ee028b7e..c7efcd4dc3 100644 --- a/crates/ruff_python_formatter/src/other/parameters.rs +++ b/crates/ruff_python_formatter/src/other/parameters.rs @@ -7,8 +7,8 @@ use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer}; use ruff_text_size::{TextRange, TextSize}; use crate::comments::{ - dangling_open_parenthesis_comments, leading_comments, leading_node_comments, trailing_comments, - CommentLinePosition, SourceComment, + dangling_comments, dangling_open_parenthesis_comments, leading_comments, leading_node_comments, + trailing_comments, CommentLinePosition, SourceComment, }; use crate::context::{NodeLevel, WithNodeLevel}; use crate::expression::parentheses::empty_parenthesized; @@ -242,7 +242,7 @@ impl FormatNodeRule for FormatParameters { + usize::from(kwarg.is_some()); if self.parentheses == ParametersParentheses::Never { - write!(f, [group(&format_inner)]) + write!(f, [group(&format_inner), dangling_comments(dangling)]) } else if num_parameters == 0 { // No parameters, format any dangling comments between `()` write!(f, [empty_parenthesized("(", dangling, ")")]) diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__lambda.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__lambda.py.snap index 0c1ec4565e..73e3bcc04d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__lambda.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__lambda.py.snap @@ -62,12 +62,16 @@ lambda x: lambda y: lambda z: ( z) # Trailing # Trailing - a = ( lambda # Dangling : 1 ) +a = ( + lambda x # Dangling + , y: 1 +) + # Regression test: lambda empty arguments ranges were too long, leading to unstable # formatting (lambda:(# @@ -99,6 +103,27 @@ lambda a, *args, b, **kwds,: 0 lambda a, *, b, **kwds,: 0 lambda a, /: a lambda a, /, c: a + +# Dangling comments without parameters. +( + lambda + : # 3 + None +) + +( + lambda + # 3 + : None +) + +( + lambda # 1 + # 2 + : # 3 + # 4 + None # 5 +) ``` ## Output @@ -159,9 +184,14 @@ lambda x: lambda y: lambda z: ( ) # Trailing # Trailing +a = ( + lambda: # Dangling + 1 +) a = ( - lambda: 1 # Dangling + lambda x, # Dangling + y: 1 ) # Regression test: lambda empty arguments ranges were too long, leading to unstable @@ -188,7 +218,7 @@ lambda **kwds,: 0 lambda a, *args,: 0 lambda a, **kwds,: 0 lambda *args, b,: 0 -lambda: 0 +lambda *, b,: 0 lambda *args, **kwds,: 0 lambda a, *args, b,: 0 lambda a, *, b,: 0 @@ -199,6 +229,25 @@ lambda a, *args, b, **kwds,: 0 lambda a, *, b, **kwds,: 0 lambda a, /: a lambda a, /, c: a + +# Dangling comments without parameters. +( + lambda: # 3 + None +) + +( + lambda: # 3 + None +) + +( + lambda: # 1 + # 2 + # 3 + # 4 + None # 5 +) ``` diff --git a/crates/ruff_python_parser/src/python.lalrpop b/crates/ruff_python_parser/src/python.lalrpop index 068ddec468..fead7393ab 100644 --- a/crates/ruff_python_parser/src/python.lalrpop +++ b/crates/ruff_python_parser/src/python.lalrpop @@ -1354,14 +1354,12 @@ NamedExpression: ast::Expr = { }; LambdaDef: ast::Expr = { - "lambda" ?> ":" > =>? { - p.as_ref().map(validate_arguments).transpose()?; - let p = p - .unwrap_or_else(|| ast::Parameters::empty((location_args..end_location_args).into())); + "lambda" ?> ":" > =>? { + parameters.as_ref().map(validate_arguments).transpose()?; Ok(ast::Expr::Lambda( ast::ExprLambda { - parameters: Box::new(p), + parameters: parameters.map(Box::new), body: Box::new(body), range: (location..end_location).into() } diff --git a/crates/ruff_python_parser/src/python.rs b/crates/ruff_python_parser/src/python.rs index c98a1147b4..6682cde8db 100644 --- a/crates/ruff_python_parser/src/python.rs +++ b/crates/ruff_python_parser/src/python.rs @@ -1,5 +1,5 @@ // auto-generated: "lalrpop 0.20.0" -// sha3: e2b75c2709648429269934006c032842f6800678baccab09dedfb746463c30b4 +// sha3: 6ec03d3255ad27917601bff9ad0b1df5f8702124139879a78e7dca689ca1b113 use num_bigint::BigInt; use ruff_text_size::TextSize; use ruff_python_ast::{self as ast, Ranged, IpyEscapeKind}; @@ -35817,7 +35817,7 @@ fn __action184< (_, location, _): (TextSize, TextSize, TextSize), (_, _, _): (TextSize, token::Tok, TextSize), (_, location_args, _): (TextSize, TextSize, TextSize), - (_, p, _): (TextSize, core::option::Option, TextSize), + (_, parameters, _): (TextSize, core::option::Option, TextSize), (_, end_location_args, _): (TextSize, TextSize, TextSize), (_, _, _): (TextSize, token::Tok, TextSize), (_, body, _): (TextSize, ast::Expr, TextSize), @@ -35825,13 +35825,11 @@ fn __action184< ) -> Result> { { - p.as_ref().map(validate_arguments).transpose()?; - let p = p - .unwrap_or_else(|| ast::Parameters::empty((location_args..end_location_args).into())); + parameters.as_ref().map(validate_arguments).transpose()?; Ok(ast::Expr::Lambda( ast::ExprLambda { - parameters: Box::new(p), + parameters: parameters.map(Box::new), body: Box::new(body), range: (location..end_location).into() } diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args.snap index 880b08b7a3..f5d230cfc2 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args.snap @@ -10,51 +10,53 @@ Ok( value: Lambda( ExprLambda { range: 0..20, - parameters: Parameters { - range: 7..17, - posonlyargs: [], - args: [], - vararg: None, - kwonlyargs: [ - ParameterWithDefault { - range: 10..11, - parameter: Parameter { + parameters: Some( + Parameters { + range: 7..17, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [ + ParameterWithDefault { range: 10..11, - name: Identifier { - id: "a", + parameter: Parameter { range: 10..11, + name: Identifier { + id: "a", + range: 10..11, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 13..14, - parameter: Parameter { + ParameterWithDefault { range: 13..14, - name: Identifier { - id: "b", + parameter: Parameter { range: 13..14, + name: Identifier { + id: "b", + range: 13..14, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 16..17, - parameter: Parameter { + ParameterWithDefault { range: 16..17, - name: Identifier { - id: "c", + parameter: Parameter { range: 16..17, + name: Identifier { + id: "c", + range: 16..17, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - kwarg: None, - }, + ], + kwarg: None, + }, + ), body: Constant( ExprConstant { range: 19..20, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args_with_defaults.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args_with_defaults.snap index 0e903e5e3c..838ab8abd4 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args_with_defaults.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_kw_only_args_with_defaults.snap @@ -10,71 +10,73 @@ Ok( value: Lambda( ExprLambda { range: 0..26, - parameters: Parameters { - range: 7..23, - posonlyargs: [], - args: [], - vararg: None, - kwonlyargs: [ - ParameterWithDefault { - range: 10..11, - parameter: Parameter { + parameters: Some( + Parameters { + range: 7..23, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [ + ParameterWithDefault { range: 10..11, - name: Identifier { - id: "a", + parameter: Parameter { range: 10..11, + name: Identifier { + id: "a", + range: 10..11, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 13..17, - parameter: Parameter { - range: 13..14, - name: Identifier { - id: "b", + ParameterWithDefault { + range: 13..17, + parameter: Parameter { range: 13..14, - }, - annotation: None, - }, - default: Some( - Constant( - ExprConstant { - range: 15..17, - value: Int( - 20, - ), - kind: None, + name: Identifier { + id: "b", + range: 13..14, }, + annotation: None, + }, + default: Some( + Constant( + ExprConstant { + range: 15..17, + value: Int( + 20, + ), + kind: None, + }, + ), ), - ), - }, - ParameterWithDefault { - range: 19..23, - parameter: Parameter { - range: 19..20, - name: Identifier { - id: "c", + }, + ParameterWithDefault { + range: 19..23, + parameter: Parameter { range: 19..20, - }, - annotation: None, - }, - default: Some( - Constant( - ExprConstant { - range: 21..23, - value: Int( - 30, - ), - kind: None, + name: Identifier { + id: "c", + range: 19..20, }, + annotation: None, + }, + default: Some( + Constant( + ExprConstant { + range: 21..23, + value: Int( + 30, + ), + kind: None, + }, + ), ), - ), - }, - ], - kwarg: None, - }, + }, + ], + kwarg: None, + }, + ), body: Constant( ExprConstant { range: 25..26, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_no_args.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_no_args.snap index daa1f9ae29..4b90995b00 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_no_args.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_no_args.snap @@ -10,14 +10,7 @@ Ok( value: Lambda( ExprLambda { range: 0..9, - parameters: Parameters { - range: 6..6, - posonlyargs: [], - args: [], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + parameters: None, body: Constant( ExprConstant { range: 8..9, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_and_kw_only_args.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_and_kw_only_args.snap index 66f60fe7d4..a3eff7188b 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_and_kw_only_args.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_and_kw_only_args.snap @@ -10,76 +10,78 @@ Ok( value: Lambda( ExprLambda { range: 0..26, - parameters: Parameters { - range: 7..23, - posonlyargs: [], - args: [ - ParameterWithDefault { - range: 7..8, - parameter: Parameter { + parameters: Some( + Parameters { + range: 7..23, + posonlyargs: [], + args: [ + ParameterWithDefault { range: 7..8, - name: Identifier { - id: "a", + parameter: Parameter { range: 7..8, + name: Identifier { + id: "a", + range: 7..8, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 10..11, - parameter: Parameter { + ParameterWithDefault { range: 10..11, - name: Identifier { - id: "b", + parameter: Parameter { range: 10..11, + name: Identifier { + id: "b", + range: 10..11, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 13..14, - parameter: Parameter { + ParameterWithDefault { range: 13..14, - name: Identifier { - id: "c", + parameter: Parameter { range: 13..14, + name: Identifier { + id: "c", + range: 13..14, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - vararg: None, - kwonlyargs: [ - ParameterWithDefault { - range: 19..20, - parameter: Parameter { + ], + vararg: None, + kwonlyargs: [ + ParameterWithDefault { range: 19..20, - name: Identifier { - id: "d", + parameter: Parameter { range: 19..20, + name: Identifier { + id: "d", + range: 19..20, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 22..23, - parameter: Parameter { + ParameterWithDefault { range: 22..23, - name: Identifier { - id: "e", + parameter: Parameter { range: 22..23, + name: Identifier { + id: "e", + range: 22..23, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - kwarg: None, - }, + ], + kwarg: None, + }, + ), body: Constant( ExprConstant { range: 25..26, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args.snap index 08563620f3..1f8c1d0d3a 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args.snap @@ -10,51 +10,53 @@ Ok( value: Lambda( ExprLambda { range: 0..17, - parameters: Parameters { - range: 7..14, - posonlyargs: [], - args: [ - ParameterWithDefault { - range: 7..8, - parameter: Parameter { + parameters: Some( + Parameters { + range: 7..14, + posonlyargs: [], + args: [ + ParameterWithDefault { range: 7..8, - name: Identifier { - id: "a", + parameter: Parameter { range: 7..8, + name: Identifier { + id: "a", + range: 7..8, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 10..11, - parameter: Parameter { + ParameterWithDefault { range: 10..11, - name: Identifier { - id: "b", + parameter: Parameter { range: 10..11, + name: Identifier { + id: "b", + range: 10..11, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 13..14, - parameter: Parameter { + ParameterWithDefault { range: 13..14, - name: Identifier { - id: "c", + parameter: Parameter { range: 13..14, + name: Identifier { + id: "c", + range: 13..14, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + ], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + ), body: Constant( ExprConstant { range: 16..17, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args_with_defaults.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args_with_defaults.snap index 138c713437..9760d71f28 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args_with_defaults.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__function__tests__lambda_pos_args_with_defaults.snap @@ -10,71 +10,73 @@ Ok( value: Lambda( ExprLambda { range: 0..23, - parameters: Parameters { - range: 7..20, - posonlyargs: [], - args: [ - ParameterWithDefault { - range: 7..8, - parameter: Parameter { + parameters: Some( + Parameters { + range: 7..20, + posonlyargs: [], + args: [ + ParameterWithDefault { range: 7..8, - name: Identifier { - id: "a", + parameter: Parameter { range: 7..8, + name: Identifier { + id: "a", + range: 7..8, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 10..14, - parameter: Parameter { - range: 10..11, - name: Identifier { - id: "b", + ParameterWithDefault { + range: 10..14, + parameter: Parameter { range: 10..11, - }, - annotation: None, - }, - default: Some( - Constant( - ExprConstant { - range: 12..14, - value: Int( - 20, - ), - kind: None, + name: Identifier { + id: "b", + range: 10..11, }, + annotation: None, + }, + default: Some( + Constant( + ExprConstant { + range: 12..14, + value: Int( + 20, + ), + kind: None, + }, + ), ), - ), - }, - ParameterWithDefault { - range: 16..20, - parameter: Parameter { - range: 16..17, - name: Identifier { - id: "c", + }, + ParameterWithDefault { + range: 16..20, + parameter: Parameter { range: 16..17, - }, - annotation: None, - }, - default: Some( - Constant( - ExprConstant { - range: 18..20, - value: Int( - 30, - ), - kind: None, + name: Identifier { + id: "c", + range: 16..17, }, + annotation: None, + }, + default: Some( + Constant( + ExprConstant { + range: 18..20, + value: Int( + 30, + ), + kind: None, + }, + ), ), - ), - }, - ], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + }, + ], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + ), body: Constant( ExprConstant { range: 22..23, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__match_as_identifier.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__match_as_identifier.snap index e067e4d361..38a83842e2 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__match_as_identifier.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__match_as_identifier.snap @@ -727,27 +727,29 @@ expression: "parse_suite(source, \"\").unwrap()" value: Lambda( ExprLambda { range: 591..619, - parameters: Parameters { - range: 598..603, - posonlyargs: [], - args: [ - ParameterWithDefault { - range: 598..603, - parameter: Parameter { + parameters: Some( + Parameters { + range: 598..603, + posonlyargs: [], + args: [ + ParameterWithDefault { range: 598..603, - name: Identifier { - id: "query", + parameter: Parameter { range: 598..603, + name: Identifier { + id: "query", + range: 598..603, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + ], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + ), body: Compare( ExprCompare { range: 605..619, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda.snap index a7e23a899e..4401fd6c84 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda.snap @@ -9,39 +9,41 @@ expression: parse_ast value: Lambda( ExprLambda { range: 0..18, - parameters: Parameters { - range: 7..11, - posonlyargs: [], - args: [ - ParameterWithDefault { - range: 7..8, - parameter: Parameter { + parameters: Some( + Parameters { + range: 7..11, + posonlyargs: [], + args: [ + ParameterWithDefault { range: 7..8, - name: Identifier { - id: "x", + parameter: Parameter { range: 7..8, + name: Identifier { + id: "x", + range: 7..8, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ParameterWithDefault { - range: 10..11, - parameter: Parameter { + ParameterWithDefault { range: 10..11, - name: Identifier { - id: "y", + parameter: Parameter { range: 10..11, + name: Identifier { + id: "y", + range: 10..11, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + ], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + ), body: BinOp( ExprBinOp { range: 13..18, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda_no_args.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda_no_args.snap index 970316e846..c8c73c80c8 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda_no_args.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__parse_lambda_no_args.snap @@ -9,14 +9,7 @@ expression: parse_ast value: Lambda( ExprLambda { range: 0..9, - parameters: Parameters { - range: 6..6, - posonlyargs: [], - args: [], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + parameters: None, body: Constant( ExprConstant { range: 8..9, diff --git a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__type_as_identifier.snap b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__type_as_identifier.snap index 3c2a82b998..8b10562a32 100644 --- a/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__type_as_identifier.snap +++ b/crates/ruff_python_parser/src/snapshots/ruff_python_parser__parser__tests__type_as_identifier.snap @@ -663,27 +663,29 @@ expression: "parse_suite(source, \"\").unwrap()" value: Lambda( ExprLambda { range: 507..535, - parameters: Parameters { - range: 514..519, - posonlyargs: [], - args: [ - ParameterWithDefault { - range: 514..519, - parameter: Parameter { + parameters: Some( + Parameters { + range: 514..519, + posonlyargs: [], + args: [ + ParameterWithDefault { range: 514..519, - name: Identifier { - id: "query", + parameter: Parameter { range: 514..519, + name: Identifier { + id: "query", + range: 514..519, + }, + annotation: None, }, - annotation: None, + default: None, }, - default: None, - }, - ], - vararg: None, - kwonlyargs: [], - kwarg: None, - }, + ], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + ), body: Compare( ExprCompare { range: 521..535,