diff --git a/parser/src/error.rs b/parser/src/error.rs index fc53ebb0d..c946ee854 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -21,6 +21,7 @@ pub enum LexicalErrorType { UnicodeError, NestingError, PositionalArgumentError, + DuplicateKeywordArgumentError, UnrecognizedToken { tok: char }, FStringError(FStringErrorType), OtherError(String), @@ -33,6 +34,9 @@ impl fmt::Display for LexicalErrorType { LexicalErrorType::FStringError(error) => write!(f, "Got error in f-string: {}", error), LexicalErrorType::UnicodeError => write!(f, "Got unexpected unicode"), LexicalErrorType::NestingError => write!(f, "Got unexpected nesting"), + LexicalErrorType::DuplicateKeywordArgumentError => { + write!(f, "keyword argument repeated") + } LexicalErrorType::PositionalArgumentError => { write!(f, "positional argument follows keyword argument") } diff --git a/parser/src/function.rs b/parser/src/function.rs index 552bdfd10..d20cf8a9a 100644 --- a/parser/src/function.rs +++ b/parser/src/function.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use crate::ast; use crate::error::{LexicalError, LexicalErrorType}; @@ -6,9 +8,22 @@ type FunctionArgument = (Option>, ast::Expression); pub fn parse_args(func_args: Vec) -> Result { let mut args = vec![]; let mut keywords = vec![]; + + let mut keyword_names = HashSet::with_capacity(func_args.len()); for (name, value) in func_args { match name { Some(n) => { + if let Some(keyword_name) = n.clone() { + if keyword_names.contains(&keyword_name) { + return Err(LexicalError { + error: LexicalErrorType::DuplicateKeywordArgumentError, + location: value.location.clone(), + }); + } + + keyword_names.insert(keyword_name.clone()); + } + keywords.push(ast::Keyword { name: n, value }); } None => { diff --git a/tests/snippets/function_args.py b/tests/snippets/function_args.py index 903a43f9b..4441c9416 100644 --- a/tests/snippets/function_args.py +++ b/tests/snippets/function_args.py @@ -95,3 +95,10 @@ assert kwargs == [('a', 1), ('b', 2)] kwargs = func(a=1, b=2, c=3) assert kwargs == [('a', 1), ('b', 2), ('c', 3)] + + +def inc(n): + return n + 1 + +with assert_raises(SyntaxError): + exec("inc(n=1, n=2)")