Prohibit starred arguments after double-starred arguments

CPython prohibits ‘f(**kwargs, *args)’; we should too.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
This commit is contained in:
Anders Kaseorg 2022-12-27 01:48:05 -08:00
parent 313fd7d28c
commit 661b210391
2 changed files with 17 additions and 1 deletions

View file

@ -22,6 +22,7 @@ pub enum LexicalErrorType {
TabsAfterSpaces, TabsAfterSpaces,
DefaultArgumentError, DefaultArgumentError,
PositionalArgumentError, PositionalArgumentError,
UnpackedArgumentError,
DuplicateKeywordArgumentError, DuplicateKeywordArgumentError,
UnrecognizedToken { tok: char }, UnrecognizedToken { tok: char },
FStringError(FStringErrorType), FStringError(FStringErrorType),
@ -55,6 +56,12 @@ impl fmt::Display for LexicalErrorType {
LexicalErrorType::PositionalArgumentError => { LexicalErrorType::PositionalArgumentError => {
write!(f, "positional argument follows keyword argument") write!(f, "positional argument follows keyword argument")
} }
LexicalErrorType::UnpackedArgumentError => {
write!(
f,
"iterable argument unpacking follows keyword argument unpacking"
)
}
LexicalErrorType::UnrecognizedToken { tok } => { LexicalErrorType::UnrecognizedToken { tok } => {
write!(f, "Got unexpected token {tok}") write!(f, "Got unexpected token {tok}")
} }

View file

@ -55,6 +55,7 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
let mut keyword_names = let mut keyword_names =
FxHashSet::with_capacity_and_hasher(func_args.len(), Default::default()); FxHashSet::with_capacity_and_hasher(func_args.len(), Default::default());
let mut double_starred = false;
for (name, value) in func_args { for (name, value) in func_args {
match name { match name {
Some((start, end, name)) => { Some((start, end, name)) => {
@ -67,6 +68,8 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
} }
keyword_names.insert(keyword_name.clone()); keyword_names.insert(keyword_name.clone());
} else {
double_starred = true;
} }
keywords.push(ast::Keyword::new( keywords.push(ast::Keyword::new(
@ -76,12 +79,18 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
)); ));
} }
None => { None => {
// Allow starred args after keyword arguments. // Allow starred arguments after keyword arguments but
// not after double-starred arguments.
if !keywords.is_empty() && !is_starred(&value) { if !keywords.is_empty() && !is_starred(&value) {
return Err(LexicalError { return Err(LexicalError {
error: LexicalErrorType::PositionalArgumentError, error: LexicalErrorType::PositionalArgumentError,
location: value.location, location: value.location,
}); });
} else if double_starred {
return Err(LexicalError {
error: LexicalErrorType::UnpackedArgumentError,
location: value.location,
});
} }
args.push(value); args.push(value);