From 21669a023bee939af0114291c4e1b8edb0d7d96e Mon Sep 17 00:00:00 2001 From: "Vazrupe (HyeonGyu Lee)" Date: Mon, 23 Sep 2019 18:24:47 +0900 Subject: [PATCH] Add duplicate keyword argument error Fixes #116 --- Cargo.lock | 1 + parser/src/error.rs | 4 ++++ parser/src/function.rs | 15 +++++++++++++++ tests/snippets/function_args.py | 7 +++++++ 4 files changed, 27 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1dc654460..02c6349b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,6 +1221,7 @@ dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython-compiler 0.1.0", "rustpython-parser 0.1.0", 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)")