11use ruff_diagnostics:: { Diagnostic , Edit , Fix , FixAvailability , Violation } ;
22use ruff_macros:: { derive_message_formats, ViolationMetadata } ;
3+ use ruff_python_ast:: parenthesize:: parenthesized_range;
34use ruff_python_ast:: {
4- self as ast, Expr , Identifier , Parameter , ParameterWithDefault , Parameters , Stmt ,
5+ self as ast, AstNode , Expr , ExprEllipsisLiteral , ExprLambda , Identifier , Parameter ,
6+ ParameterWithDefault , Parameters , Stmt ,
57} ;
6- use ruff_python_codegen:: Generator ;
78use ruff_python_semantic:: SemanticModel ;
89use ruff_python_trivia:: { has_leading_content, has_trailing_content, leading_indentation} ;
910use ruff_source_file:: UniversalNewlines ;
@@ -65,10 +66,7 @@ pub(crate) fn lambda_assignment(
6566 return ;
6667 } ;
6768
68- let Expr :: Lambda ( ast:: ExprLambda {
69- parameters, body, ..
70- } ) = value
71- else {
69+ let Expr :: Lambda ( lambda) = value else {
7270 return ;
7371 } ;
7472
@@ -85,16 +83,9 @@ pub(crate) fn lambda_assignment(
8583 let first_line = checker. locator ( ) . line_str ( stmt. start ( ) ) ;
8684 let indentation = leading_indentation ( first_line) ;
8785 let mut indented = String :: new ( ) ;
88- for ( idx, line) in function (
89- id,
90- parameters. as_deref ( ) ,
91- body,
92- annotation,
93- checker. semantic ( ) ,
94- checker. generator ( ) ,
95- )
96- . universal_newlines ( )
97- . enumerate ( )
86+ for ( idx, line) in function ( id, lambda, annotation, checker)
87+ . universal_newlines ( )
88+ . enumerate ( )
9889 {
9990 if idx == 0 {
10091 indented. push_str ( & line) ;
@@ -186,19 +177,21 @@ fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec<Exp
186177/// Generate a function definition from a `lambda` expression.
187178fn function (
188179 name : & str ,
189- parameters : Option < & Parameters > ,
190- body : & Expr ,
180+ lambda : & ExprLambda ,
191181 annotation : Option < & Expr > ,
192- semantic : & SemanticModel ,
193- generator : Generator ,
182+ checker : & Checker ,
194183) -> String {
184+ // Use a dummy body. It gets replaced at the end with the actual body.
185+ // This allows preserving the source formatting for the body.
195186 let body = Stmt :: Return ( ast:: StmtReturn {
196- value : Some ( Box :: new ( body. clone ( ) ) ) ,
187+ value : Some ( Box :: new ( Expr :: EllipsisLiteral (
188+ ExprEllipsisLiteral :: default ( ) ,
189+ ) ) ) ,
197190 range : TextRange :: default ( ) ,
198191 } ) ;
199- let parameters = parameters. cloned ( ) . unwrap_or_default ( ) ;
192+ let parameters = lambda . parameters . as_deref ( ) . cloned ( ) . unwrap_or_default ( ) ;
200193 if let Some ( annotation) = annotation {
201- if let Some ( ( arg_types, return_type) ) = extract_types ( annotation, semantic) {
194+ if let Some ( ( arg_types, return_type) ) = extract_types ( annotation, checker . semantic ( ) ) {
202195 // A `lambda` expression can only have positional-only and positional-or-keyword
203196 // arguments. The order is always positional-only first, then positional-or-keyword.
204197 let new_posonlyargs = parameters
@@ -243,10 +236,12 @@ fn function(
243236 type_params : None ,
244237 range : TextRange :: default ( ) ,
245238 } ) ;
246- return generator. stmt ( & func) ;
239+ let generated = checker. generator ( ) . stmt ( & func) ;
240+
241+ return replace_trailing_ellipsis_with_original_expr ( generated, lambda, checker) ;
247242 }
248243 }
249- let func = Stmt :: FunctionDef ( ast:: StmtFunctionDef {
244+ let function = Stmt :: FunctionDef ( ast:: StmtFunctionDef {
250245 is_async : false ,
251246 name : Identifier :: new ( name. to_string ( ) , TextRange :: default ( ) ) ,
252247 parameters : Box :: new ( parameters) ,
@@ -256,5 +251,32 @@ fn function(
256251 type_params : None ,
257252 range : TextRange :: default ( ) ,
258253 } ) ;
259- generator. stmt ( & func)
254+ let generated = checker. generator ( ) . stmt ( & function) ;
255+
256+ replace_trailing_ellipsis_with_original_expr ( generated, lambda, checker)
257+ }
258+
259+ fn replace_trailing_ellipsis_with_original_expr (
260+ mut generated : String ,
261+ lambda : & ExprLambda ,
262+ checker : & Checker ,
263+ ) -> String {
264+ let original_expr_range = parenthesized_range (
265+ ( & lambda. body ) . into ( ) ,
266+ lambda. as_any_node_ref ( ) ,
267+ checker. comment_ranges ( ) ,
268+ checker. source ( ) ,
269+ )
270+ . unwrap_or ( lambda. body . range ( ) ) ;
271+
272+ let original_expr_in_source = checker. locator ( ) . slice ( original_expr_range) ;
273+
274+ let placeholder_ellipsis_start = generated. rfind ( "..." ) . unwrap ( ) ;
275+ let placeholder_ellipsis_end = placeholder_ellipsis_start + "..." . len ( ) ;
276+
277+ generated. replace_range (
278+ placeholder_ellipsis_start..placeholder_ellipsis_end,
279+ original_expr_in_source,
280+ ) ;
281+ generated
260282}
0 commit comments