diff --git a/boa/src/syntax/parser/function/mod.rs b/boa/src/syntax/parser/function/mod.rs index ee3463cef3..abc2f780cc 100644 --- a/boa/src/syntax/parser/function/mod.rs +++ b/boa/src/syntax/parser/function/mod.rs @@ -12,10 +12,7 @@ mod tests; use crate::{ syntax::{ - ast::{ - node::{self}, - Punctuator, - }, + ast::{node, Punctuator}, lexer::{InputElement, TokenKind}, parser::{ expression::Initializer, @@ -25,7 +22,7 @@ use crate::{ }, BoaProfiler, }; -use std::io::Read; +use std::{collections::HashSet, io::Read}; /// Formal parameters parsing. /// @@ -66,6 +63,7 @@ where cursor.set_goal(InputElement::RegExp); let mut params = Vec::new(); + let mut param_names = HashSet::new(); if cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.kind() == &TokenKind::Punctuator(Punctuator::CloseParen) @@ -76,6 +74,7 @@ where loop { let mut rest_param = false; + let position = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.span().start(); let next_param = match cursor.peek(0)? { Some(tok) if tok.kind() == &TokenKind::Punctuator(Punctuator::Spread) => { rest_param = true; @@ -83,7 +82,11 @@ where } _ => FormalParameter::new(self.allow_yield, self.allow_await).parse(cursor)?, }; + if param_names.contains(next_param.name()) { + return Err(ParseError::general("duplicate parameter name", position)); + } + param_names.insert(Box::from(next_param.name())); params.push(next_param); if cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.kind() diff --git a/boa/src/syntax/parser/function/tests.rs b/boa/src/syntax/parser/function/tests.rs index a5d7c0b8bb..b6464dfce5 100644 --- a/boa/src/syntax/parser/function/tests.rs +++ b/boa/src/syntax/parser/function/tests.rs @@ -4,7 +4,7 @@ use crate::syntax::{ Identifier, Node, Return, }, ast::op::NumOp, - parser::tests::check_parser, + parser::{tests::check_parser, Parser}, }; /// Checks basic function declaration parsing. @@ -21,6 +21,16 @@ fn check_basic() { ); } +/// Checks for duplicate parameter names. +#[test] +fn check_duplicates() { + let js = "function foo(a, a) {}"; + + let res = Parser::new(js.as_bytes(), false).parse_all(); + dbg!(&res); + assert!(res.is_err()); +} + /// Checks basic function declaration parsing with automatic semicolon insertion. #[test] fn check_basic_semicolon_insertion() { diff --git a/boa/src/syntax/parser/mod.rs b/boa/src/syntax/parser/mod.rs index f6b677a77c..3058cf5501 100644 --- a/boa/src/syntax/parser/mod.rs +++ b/boa/src/syntax/parser/mod.rs @@ -153,6 +153,6 @@ where type Output = StatementList; fn parse(self, cursor: &mut Cursor) -> Result { - self::statement::StatementList::new(false, false, false, true, &[]).parse(cursor) + self::statement::StatementList::new(false, false, false, false, &[]).parse(cursor) } }