Skip to content

Commit a067d87

Browse files
authored
Fix incorrect Parameter range for *args and **kwargs (#10283)
## Summary Fix #10282 This PR updates the Python grammar to include the `*` character in `*args` `**kwargs` in the range of the `Parameter` ``` def f(*args, **kwargs): pass # ~~~~ ~~~~~~ <-- range before the PR # ^^^^^ ^^^^^^^^ <-- range after ``` The invalid syntax `def f(*, **kwargs): ...` is also now correctly reported. ## Test Plan Test cases were added to `function.rs`.
1 parent b64f2ea commit a067d87

File tree

23 files changed

+14760
-14768
lines changed

23 files changed

+14760
-14768
lines changed

crates/ruff_linter/src/checkers/ast/analyze/parameter.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::rules::{flake8_builtins, pep8_naming, pycodestyle};
99
pub(crate) fn parameter(parameter: &Parameter, checker: &mut Checker) {
1010
if checker.enabled(Rule::AmbiguousVariableName) {
1111
if let Some(diagnostic) =
12-
pycodestyle::rules::ambiguous_variable_name(&parameter.name, parameter.range())
12+
pycodestyle::rules::ambiguous_variable_name(&parameter.name, parameter.name.range())
1313
{
1414
checker.diagnostics.push(diagnostic);
1515
}

crates/ruff_linter/src/rules/flake8_builtins/rules/builtin_argument_shadowing.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub(crate) fn builtin_argument_shadowing(checker: &mut Checker, parameter: &Para
7373
BuiltinArgumentShadowing {
7474
name: parameter.name.to_string(),
7575
},
76-
parameter.range(),
76+
parameter.name.range(),
7777
));
7878
}
7979
}

crates/ruff_python_formatter/src/comments/placement.rs

+8
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ fn handle_enclosed_comment<'a>(
202202
}
203203
})
204204
}
205+
AnyNodeRef::Parameter(parameter) => {
206+
// E.g. a comment between the `*` or `**` and the parameter name.
207+
if comment.preceding_node().is_none() || comment.following_node().is_none() {
208+
CommentPlacement::leading(parameter, comment)
209+
} else {
210+
CommentPlacement::Default(comment)
211+
}
212+
}
205213
AnyNodeRef::Arguments(_) | AnyNodeRef::TypeParams(_) | AnyNodeRef::PatternArguments(_) => {
206214
handle_bracketed_end_of_line_comment(comment, locator)
207215
}

crates/ruff_python_parser/src/function.rs

+19-9
Original file line numberDiff line numberDiff line change
@@ -176,19 +176,27 @@ mod tests {
176176
function_and_lambda! {
177177
test_function_no_args: "def f(): pass",
178178
test_function_pos_args: "def f(a, b, c): pass",
179-
test_function_pos_args_with_defaults: "def f(a, b=20, c=30): pass",
179+
test_function_posonly_and_pos_args: "def f(a, /, b, c): pass",
180+
test_function_pos_args_with_defaults: "def f(a, b=20, /, c=30): pass",
181+
test_function_pos_args_with_defaults_and_varargs_and_kwargs: "def f(a, b=20, /, c=30, *args, **kwargs): pass",
180182
test_function_kw_only_args: "def f(*, a, b, c): pass",
181183
test_function_kw_only_args_with_defaults: "def f(*, a, b=20, c=30): pass",
182-
test_function_pos_and_kw_only_args: "def f(a, b, c, *, d, e, f): pass",
183-
test_function_pos_and_kw_only_args_with_defaults: "def f(a, b, c, *, d, e=20, f=30): pass",
184-
test_function_pos_and_kw_only_args_with_defaults_and_varargs: "def f(a, b, c, *args, d, e=20, f=30): pass",
185-
test_function_pos_and_kw_only_args_with_defaults_and_varargs_and_kwargs: "def f(a, b, c, *args, d, e=20, f=30, **kwargs): pass",
184+
test_function_kw_only_args_with_defaults_and_varargs: "def f(*args, a, b=20, c=30): pass",
185+
test_function_kw_only_args_with_defaults_and_kwargs: "def f(*, a, b=20, c=30, **kwargs): pass",
186+
test_function_kw_only_args_with_defaults_and_varargs_and_kwargs: "def f(*args, a, b=20, c=30, **kwargs): pass",
187+
test_function_pos_and_kw_only_args: "def f(a, b, /, c, *, d, e, f): pass",
188+
test_function_pos_and_kw_only_args_with_defaults: "def f(a, b, /, c, *, d, e=20, f=30): pass",
189+
test_function_pos_and_kw_only_args_with_defaults_and_varargs: "def f(a, b, /, c, *args, d, e=20, f=30): pass",
190+
test_function_pos_and_kw_only_args_with_defaults_and_kwargs: "def f(a, b, /, c, *, d, e=20, f=30, **kwargs): pass",
191+
test_function_pos_and_kw_only_args_with_defaults_and_varargs_and_kwargs: "def f(a, b, /, c, *args, d, e=20, f=30, **kwargs): pass",
186192
test_lambda_no_args: "lambda: 1",
187193
test_lambda_pos_args: "lambda a, b, c: 1",
188-
test_lambda_pos_args_with_defaults: "lambda a, b=20, c=30: 1",
194+
test_lambda_posonly_args: "lambda a, b, /, c: 1",
195+
test_lambda_pos_args_with_defaults: "lambda a, b=20, /, c=30: 1",
189196
test_lambda_kw_only_args: "lambda *, a, b, c: 1",
190197
test_lambda_kw_only_args_with_defaults: "lambda *, a, b=20, c=30: 1",
191-
test_lambda_pos_and_kw_only_args: "lambda a, b, c, *, d, e: 0",
198+
test_lambda_pos_and_kw_only_args: "lambda a, b, /, c, *, d, e: 0",
199+
test_lambda_pos_and_kw_only_args_and_vararg_and_kwarg: "lambda a, b, /, c, *d, e, **f: 0",
192200
}
193201

194202
fn function_parse_error(src: &str) -> LexicalErrorType {
@@ -219,14 +227,16 @@ mod tests {
219227
test_duplicates_f2: "def f(a, *, a): pass", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
220228
test_duplicates_f3: "def f(a, a=20): pass", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
221229
test_duplicates_f4: "def f(a, *a): pass", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
222-
test_duplicates_f5: "def f(a, *, **a): pass", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
230+
test_duplicates_f5: "def f(a, *, b, **a): pass", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
223231
test_duplicates_l1: "lambda a, a: 1", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
224232
test_duplicates_l2: "lambda a, *, a: 1", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
225233
test_duplicates_l3: "lambda a, a=20: 1", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
226234
test_duplicates_l4: "lambda a, *a: 1", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
227-
test_duplicates_l5: "lambda a, *, **a: 1", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
235+
test_duplicates_l5: "lambda a, *, b, **a: 1", LexicalErrorType::DuplicateArgumentError("a".to_string().into_boxed_str()),
228236
test_default_arg_error_f: "def f(a, b=20, c): pass", LexicalErrorType::DefaultArgumentError,
229237
test_default_arg_error_l: "lambda a, b=20, c: 1", LexicalErrorType::DefaultArgumentError,
238+
test_named_arguments_follow_bare_star_1: "def f(*): pass", LexicalErrorType::OtherError("named arguments must follow bare *".to_string().into_boxed_str()),
239+
test_named_arguments_follow_bare_star_2: "def f(*, **kwargs): pass", LexicalErrorType::OtherError("named arguments must follow bare *".to_string().into_boxed_str()),
230240

231241
// Check some calls.
232242
test_positional_arg_error_f: "f(b=20, c)", LexicalErrorType::PositionalArgumentError,

crates/ruff_python_parser/src/python.lalrpop

+23-43
Original file line numberDiff line numberDiff line change
@@ -1151,24 +1151,6 @@ ParameterList<ParameterType, StarParameterType, DoubleStarParameterType>: ast::P
11511151
range: (location..end_location).into()
11521152
})
11531153
},
1154-
<location:@L> <param1:ParameterDefs<ParameterType>> <kw:("," <KwargParameter<DoubleStarParameterType>>)> ","? <end_location:@R> =>? {
1155-
validate_pos_params(&param1)?;
1156-
let (posonlyargs, args) = param1;
1157-
1158-
// Now gather rest of parameters:
1159-
let vararg = None;
1160-
let kwonlyargs = vec![];
1161-
let kwarg = kw;
1162-
1163-
Ok(ast::Parameters {
1164-
posonlyargs,
1165-
args,
1166-
kwonlyargs,
1167-
vararg,
1168-
kwarg,
1169-
range: (location..end_location).into()
1170-
})
1171-
},
11721154
<location:@L> <params:ParameterListStarArgs<ParameterType, StarParameterType, DoubleStarParameterType>> ","? <end_location:@R> => {
11731155
let (vararg, kwonlyargs, kwarg) = params;
11741156
ast::Parameters {
@@ -1180,16 +1162,6 @@ ParameterList<ParameterType, StarParameterType, DoubleStarParameterType>: ast::P
11801162
range: (location..end_location).into()
11811163
}
11821164
},
1183-
<location:@L> <kwarg:KwargParameter<DoubleStarParameterType>> ","? <end_location:@R> => {
1184-
ast::Parameters {
1185-
posonlyargs: vec![],
1186-
args: vec![],
1187-
kwonlyargs: vec![],
1188-
vararg: None,
1189-
kwarg,
1190-
range: (location..end_location).into()
1191-
}
1192-
},
11931165
};
11941166

11951167
// Use inline here to make sure the "," is not creating an ambiguity.
@@ -1219,7 +1191,11 @@ UntypedParameter: ast::ParameterWithDefault = {
12191191
},
12201192
};
12211193
StarUntypedParameter: ast::Parameter = {
1222-
<location:@L> <arg:Identifier> <end_location:@R> => ast::Parameter { name:arg, annotation: None, range: (location..end_location).into() },
1194+
<location:@L> "*" <arg:Identifier> <end_location:@R> => ast::Parameter { name:arg, annotation: None, range: (location..end_location).into() },
1195+
};
1196+
1197+
DoubleStarUntypedParameter: ast::Parameter = {
1198+
<location:@L> "**" <arg:Identifier> <end_location:@R> => ast::Parameter { name:arg, annotation: None, range: (location..end_location).into() },
12231199
};
12241200

12251201
TypedParameter: ast::ParameterWithDefault = {
@@ -1231,14 +1207,14 @@ TypedParameter: ast::ParameterWithDefault = {
12311207
};
12321208

12331209
StarTypedParameter: ast::Parameter = {
1234-
<location:@L> <name:Identifier> <annotation:(":" <TestOrStarExpr>)?> <end_location:@R> => {
1210+
<location:@L> "*" <name:Identifier> <annotation:(":" <TestOrStarExpr>)?> <end_location:@R> => {
12351211
let annotation = annotation.map(ast::Expr::from).map(Box::new);
12361212
ast::Parameter { name, annotation, range: (location..end_location).into() }
12371213
},
12381214
};
12391215

12401216
DoubleStarTypedParameter: ast::Parameter = {
1241-
<location:@L> <name:Identifier> <annotation:(":" <Test<"all">>)?> <end_location:@R> => {
1217+
<location:@L> "**" <name:Identifier> <annotation:(":" <Test<"all">>)?> <end_location:@R> => {
12421218
let annotation = annotation.map(ast::Expr::from).map(Box::new);
12431219
ast::Parameter { name, annotation, range: (location..end_location).into() }
12441220
},
@@ -1248,25 +1224,29 @@ DoubleStarTypedParameter: ast::Parameter = {
12481224
// TODO: figure out another grammar that makes this inline no longer required.
12491225
#[inline]
12501226
ParameterListStarArgs<ParameterType, StarParameterType, DoubleStarParameterType>: (Option<Box<ast::Parameter>>, Vec<ast::ParameterWithDefault>, Option<Box<ast::Parameter>>) = {
1251-
<location:@L> "*" <va:StarParameterType?> <kwonlyargs:("," <ParameterDef<ParameterType>>)*> <kwarg:("," <KwargParameter<DoubleStarParameterType>>)?> =>? {
1252-
if va.is_none() && kwonlyargs.is_empty() && kwarg.is_none() {
1227+
<location:@L> <va:StarParameterType> <kwonlyargs:("," <ParameterDef<ParameterType>>)*> <kwarg:("," <DoubleStarParameterType>)?> =>? {
1228+
let kwarg = kwarg.map(Box::new);
1229+
let va = Some(Box::new(va));
1230+
1231+
Ok((va, kwonlyargs, kwarg))
1232+
},
1233+
<location:@L> "*" <kwonlyargs:("," <ParameterDef<ParameterType>>)*> <kwarg:("," <DoubleStarParameterType>)?> =>? {
1234+
if kwonlyargs.is_empty() {
12531235
return Err(LexicalError::new(
12541236
LexicalErrorType::OtherError("named arguments must follow bare *".to_string().into_boxed_str()),
12551237
location,
12561238
))?;
12571239
}
12581240

1259-
let kwarg = kwarg.flatten();
1260-
let va = va.map(Box::new);
1241+
let kwarg = kwarg.map(Box::new);
12611242

1262-
Ok((va, kwonlyargs, kwarg))
1263-
}
1264-
};
1243+
Ok((None, kwonlyargs, kwarg))
1244+
},
1245+
<location:@L> <kwarg:(<DoubleStarParameterType>)> =>? {
1246+
let kwarg = Some(Box::new(kwarg));
12651247

1266-
KwargParameter<ParameterType>: Option<Box<ast::Parameter>> = {
1267-
"**" <kwarg:ParameterType?> => {
1268-
kwarg.map(Box::new)
1269-
}
1248+
Ok((None, vec![], kwarg))
1249+
},
12701250
};
12711251

12721252
ClassDef: ast::Stmt = {
@@ -1365,7 +1345,7 @@ NamedExpression: crate::parser::ParenthesizedExpr = {
13651345
};
13661346

13671347
LambdaDef: crate::parser::ParenthesizedExpr = {
1368-
<location:@L> "lambda" <location_args:@L> <parameters:ParameterList<UntypedParameter, StarUntypedParameter, StarUntypedParameter>?> <end_location_args:@R> ":" <fstring_middle:fstring_middle?> <body:Test<"all">> <end_location:@R> =>? {
1348+
<location:@L> "lambda" <location_args:@L> <parameters:ParameterList<UntypedParameter, StarUntypedParameter, DoubleStarUntypedParameter>?> <end_location_args:@R> ":" <fstring_middle:fstring_middle?> <body:Test<"all">> <end_location:@R> =>? {
13691349
if fstring_middle.is_some() {
13701350
return Err(LexicalError::new(
13711351
LexicalErrorType::FStringError(FStringErrorType::LambdaWithoutParentheses),

0 commit comments

Comments
 (0)