mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #4380 from nicku12345/lex_error_duplicate_function_args
add lex error for function args
This commit is contained in:
2
Lib/test/test_compile.py
vendored
2
Lib/test/test_compile.py
vendored
@@ -42,8 +42,6 @@ class TestSpecifics(unittest.TestCase):
|
||||
self.assertEqual(__debug__, prev)
|
||||
setattr(builtins, '__debug__', prev)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_argument_handling(self):
|
||||
# detect duplicate positional and keyword arguments
|
||||
self.assertRaises(SyntaxError, eval, 'lambda a,a:0')
|
||||
|
||||
6
Lib/test/test_positional_only_arg.py
vendored
6
Lib/test/test_positional_only_arg.py
vendored
@@ -22,8 +22,6 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||
with self.assertRaisesRegex(SyntaxError, regex):
|
||||
compile(codestr + "\n", "<test>", "single")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_syntax_errors(self):
|
||||
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
|
||||
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
|
||||
@@ -45,8 +43,6 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||
check_syntax_error(self, "def f(a, /, c, /, d, *, e): pass")
|
||||
check_syntax_error(self, "def f(a, *, c, /, d, e): pass")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_syntax_errors_async(self):
|
||||
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
|
||||
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
|
||||
@@ -240,8 +236,6 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||
x = lambda a, b, /, : a + b
|
||||
self.assertEqual(x(1, 2), 3)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_invalid_syntax_lambda(self):
|
||||
check_syntax_error(self, "lambda a, b = 5, /, c: None", "non-default argument follows default argument")
|
||||
check_syntax_error(self, "lambda a = 5, b, /, c: None", "non-default argument follows default argument")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use crate::{
|
||||
ast,
|
||||
error::{LexicalError, LexicalErrorType},
|
||||
function::{ArgumentList, parse_args, parse_params},
|
||||
function::{ArgumentList, parse_args, parse_params, validate_arguments},
|
||||
lexer,
|
||||
context::set_context,
|
||||
string::parse_strings,
|
||||
@@ -552,16 +552,20 @@ FuncDef: ast::Stmt = {
|
||||
};
|
||||
|
||||
Parameters: ast::Arguments = {
|
||||
"(" <a: (ParameterList<TypedParameter>)?> ")" => {
|
||||
a.unwrap_or_else(|| ast::Arguments {
|
||||
posonlyargs: vec![],
|
||||
args: vec![],
|
||||
vararg: None,
|
||||
kwonlyargs: vec![],
|
||||
kw_defaults: vec![],
|
||||
kwarg: None,
|
||||
defaults: vec![]
|
||||
})
|
||||
"(" <a: (ParameterList<TypedParameter>)?> ")" =>? {
|
||||
let args = validate_arguments(
|
||||
a.unwrap_or_else(|| ast::Arguments {
|
||||
posonlyargs: vec![],
|
||||
args: vec![],
|
||||
vararg: None,
|
||||
kwonlyargs: vec![],
|
||||
kw_defaults: vec![],
|
||||
kwarg: None,
|
||||
defaults: vec![]
|
||||
})
|
||||
)?;
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -663,7 +667,7 @@ TypedParameter: ast::Arg = {
|
||||
// TODO: figure out another grammar that makes this inline no longer required.
|
||||
#[inline]
|
||||
ParameterListStarArgs<ArgType>: (Option<Box<ast::Arg>>, Vec<ast::Arg>, Vec<ast::Expr>, Option<Box<ast::Arg>>) = {
|
||||
"*" <va:ArgType?> <kw:("," ParameterDef<ArgType>)*> <kwarg:("," KwargParameter<ArgType>)?> => {
|
||||
<location:@L> "*" <va:ArgType?> <kw:("," ParameterDef<ArgType>)*> <kwarg:("," KwargParameter<ArgType>)?> =>? {
|
||||
// Extract keyword arguments:
|
||||
let mut kwonlyargs = Vec::new();
|
||||
let mut kw_defaults = Vec::new();
|
||||
@@ -678,10 +682,17 @@ ParameterListStarArgs<ArgType>: (Option<Box<ast::Arg>>, Vec<ast::Arg>, Vec<ast::
|
||||
}
|
||||
kwargs.extend(kwonlyargs.into_iter());
|
||||
|
||||
if va.is_none() && kwargs.is_empty() && kwarg.is_none() {
|
||||
Err(LexicalError {
|
||||
error: LexicalErrorType::OtherError("named arguments must follow bare *".to_string()),
|
||||
location: location,
|
||||
})?
|
||||
}
|
||||
|
||||
let kwarg = kwarg.map(|n| n.1).flatten();
|
||||
let va = va.map(Box::new);
|
||||
|
||||
(va, kwargs, kw_defaults, kwarg)
|
||||
Ok((va, kwargs, kw_defaults, kwarg))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -774,19 +785,22 @@ NamedExpression: ast::Expr = {
|
||||
};
|
||||
|
||||
LambdaDef: ast::Expr = {
|
||||
<location:@L> "lambda" <p:ParameterList<UntypedParameter>?> ":" <body:Test<"all">> <end_location:@R> => {
|
||||
let p = p.unwrap_or_else(|| {
|
||||
ast::Arguments {
|
||||
posonlyargs: vec![],
|
||||
args: vec![],
|
||||
vararg: None,
|
||||
kwonlyargs: vec![],
|
||||
kw_defaults: vec![],
|
||||
kwarg: None,
|
||||
defaults: vec![]
|
||||
<location:@L> "lambda" <p:ParameterList<UntypedParameter>?> ":" <body:Test<"all">> <end_location:@R> =>? {
|
||||
let p = validate_arguments(
|
||||
p.unwrap_or_else(|| {
|
||||
ast::Arguments {
|
||||
posonlyargs: vec![],
|
||||
args: vec![],
|
||||
vararg: None,
|
||||
kwonlyargs: vec![],
|
||||
kw_defaults: vec![],
|
||||
kwarg: None,
|
||||
defaults: vec![]
|
||||
}
|
||||
}
|
||||
});
|
||||
ast::Expr {
|
||||
))?;
|
||||
|
||||
Ok(ast::Expr {
|
||||
location,
|
||||
end_location: Some(end_location),
|
||||
custom: (),
|
||||
@@ -794,7 +808,7 @@ LambdaDef: ast::Expr = {
|
||||
args: Box::new(p),
|
||||
body: Box::new(body)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ pub enum LexicalErrorType {
|
||||
TabError,
|
||||
TabsAfterSpaces,
|
||||
DefaultArgumentError,
|
||||
DuplicateArgumentError(String),
|
||||
PositionalArgumentError,
|
||||
UnpackedArgumentError,
|
||||
DuplicateKeywordArgumentError,
|
||||
@@ -50,6 +51,9 @@ impl fmt::Display for LexicalErrorType {
|
||||
LexicalErrorType::DefaultArgumentError => {
|
||||
write!(f, "non-default argument follows default argument")
|
||||
}
|
||||
LexicalErrorType::DuplicateArgumentError(arg_name) => {
|
||||
write!(f, "duplicate argument '{arg_name}' in function definition")
|
||||
}
|
||||
LexicalErrorType::DuplicateKeywordArgumentError => {
|
||||
write!(f, "keyword argument repeated")
|
||||
}
|
||||
|
||||
@@ -10,6 +10,36 @@ pub struct ArgumentList {
|
||||
type ParameterDefs = (Vec<ast::Arg>, Vec<ast::Arg>, Vec<ast::Expr>);
|
||||
type ParameterDef = (ast::Arg, Option<ast::Expr>);
|
||||
|
||||
pub fn validate_arguments(arguments: ast::Arguments) -> Result<ast::Arguments, LexicalError> {
|
||||
let mut all_args: Vec<&ast::Located<ast::ArgData>> = vec![];
|
||||
|
||||
all_args.extend(arguments.posonlyargs.iter());
|
||||
all_args.extend(arguments.args.iter());
|
||||
|
||||
if let Some(a) = &arguments.vararg {
|
||||
all_args.push(a);
|
||||
}
|
||||
|
||||
all_args.extend(arguments.kwonlyargs.iter());
|
||||
|
||||
if let Some(a) = &arguments.kwarg {
|
||||
all_args.push(a);
|
||||
}
|
||||
|
||||
let mut all_arg_names = FxHashSet::with_hasher(Default::default());
|
||||
for arg in all_args {
|
||||
let arg_name = &arg.node.arg;
|
||||
if !all_arg_names.insert(arg_name) {
|
||||
return Err(LexicalError {
|
||||
error: LexicalErrorType::DuplicateArgumentError(arg_name.to_string()),
|
||||
location: arg.location,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(arguments)
|
||||
}
|
||||
|
||||
pub fn parse_params(
|
||||
params: (Vec<ParameterDef>, Vec<ParameterDef>),
|
||||
) -> Result<ParameterDefs, LexicalError> {
|
||||
|
||||
Reference in New Issue
Block a user