summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-11 16:11:16 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-11 16:11:16 +0200
commitcd62792c0aefffe8b0a5c7fc76e95dfa7b86a181 (patch)
tree5fdc0b4f3422eb8bc7cdb06fce8829d32ef6c9e3 /src
parent938b0af889b8fbe6265695b1b8e54aee338ba87f (diff)
Prevent duplicate named arguments and dictionary keys
Diffstat (limited to 'src')
-rw-r--r--src/parse/mod.rs29
-rw-r--r--src/parse/parser.rs4
2 files changed, 30 insertions, 3 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 3bf274f2..6d1985e0 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -12,6 +12,7 @@ pub use resolve::*;
pub use scanner::*;
pub use tokens::*;
+use std::collections::HashSet;
use std::sync::Arc;
use crate::syntax::ast::{Associativity, BinOp, UnOp};
@@ -648,9 +649,20 @@ fn array(p: &mut Parser, marker: Marker) {
/// Convert a collection into a dictionary, producing errors for anything other
/// than named pairs.
fn dict(p: &mut Parser, marker: Marker) {
+ let mut used = HashSet::new();
marker.filter_children(p, |x| match x.kind() {
kind if kind.is_paren() => Ok(()),
- NodeKind::Named | NodeKind::Comma | NodeKind::Colon | NodeKind::Spread => Ok(()),
+ NodeKind::Named => {
+ if let Some(NodeKind::Ident(ident)) =
+ x.children().first().map(|child| child.kind())
+ {
+ if !used.insert(ident.clone()) {
+ return Err("pair has duplicate key");
+ }
+ }
+ Ok(())
+ }
+ NodeKind::Comma | NodeKind::Colon | NodeKind::Spread => Ok(()),
_ => Err("expected named pair, found expression"),
});
marker.end(p, NodeKind::DictExpr);
@@ -729,9 +741,24 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
p.perform(NodeKind::CallArgs, |p| {
if p.at(&NodeKind::LeftParen) {
+ let marker = p.marker();
p.start_group(Group::Paren);
collection(p);
p.end_group();
+
+ let mut used = HashSet::new();
+ marker.filter_children(p, |x| {
+ if x.kind() == &NodeKind::Named {
+ if let Some(NodeKind::Ident(ident)) =
+ x.children().first().map(|child| child.kind())
+ {
+ if !used.insert(ident.clone()) {
+ return Err("duplicate argument");
+ }
+ }
+ }
+ Ok(())
+ });
}
while brackets && p.peek_direct() == Some(&NodeKind::LeftBracket) {
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 63ba4918..98adfba2 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -452,9 +452,9 @@ impl Marker {
}
/// Wrap all children that do not fulfill the predicate in error nodes.
- pub fn filter_children<F>(self, p: &mut Parser, f: F)
+ pub fn filter_children<F>(self, p: &mut Parser, mut f: F)
where
- F: Fn(&Green) -> Result<(), &'static str>,
+ F: FnMut(&Green) -> Result<(), &'static str>,
{
for child in &mut p.children[self.0 ..] {
// Don't expose errors.