Prevent duplicate named arguments and dictionary keys

This commit is contained in:
Laurenz 2022-04-11 16:11:16 +02:00
parent 938b0af889
commit cd62792c0a
4 changed files with 38 additions and 3 deletions

View File

@ -12,6 +12,7 @@ pub use resolve::*;
pub use scanner::*; pub use scanner::*;
pub use tokens::*; pub use tokens::*;
use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use crate::syntax::ast::{Associativity, BinOp, UnOp}; 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 /// Convert a collection into a dictionary, producing errors for anything other
/// than named pairs. /// than named pairs.
fn dict(p: &mut Parser, marker: Marker) { fn dict(p: &mut Parser, marker: Marker) {
let mut used = HashSet::new();
marker.filter_children(p, |x| match x.kind() { marker.filter_children(p, |x| match x.kind() {
kind if kind.is_paren() => Ok(()), 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"), _ => Err("expected named pair, found expression"),
}); });
marker.end(p, NodeKind::DictExpr); marker.end(p, NodeKind::DictExpr);
@ -729,9 +741,24 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
p.perform(NodeKind::CallArgs, |p| { p.perform(NodeKind::CallArgs, |p| {
if p.at(&NodeKind::LeftParen) { if p.at(&NodeKind::LeftParen) {
let marker = p.marker();
p.start_group(Group::Paren); p.start_group(Group::Paren);
collection(p); collection(p);
p.end_group(); 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) { while brackets && p.peek_direct() == Some(&NodeKind::LeftBracket) {

View File

@ -452,9 +452,9 @@ impl Marker {
} }
/// Wrap all children that do not fulfill the predicate in error nodes. /// 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 where
F: Fn(&Green) -> Result<(), &'static str>, F: FnMut(&Green) -> Result<(), &'static str>,
{ {
for child in &mut p.children[self.0 ..] { for child in &mut p.children[self.0 ..] {
// Don't expose errors. // Don't expose errors.

View File

@ -44,6 +44,10 @@
test(adder(2)(5), 7) test(adder(2)(5), 7)
} }
---
// Error: 28-47 duplicate argument
#set text(family: "Arial", family: "Helvetica")
--- ---
// Error: 2-6 expected callable or collection, found boolean // Error: 2-6 expected callable or collection, found boolean
{true()} {true()}

View File

@ -35,6 +35,10 @@
dict("b") += 1 dict("b") += 1
} }
---
// Error: 24-32 pair has duplicate key
{(first: 1, second: 2, first: 3)}
--- ---
// Simple expression after already being identified as a dictionary. // Simple expression after already being identified as a dictionary.
// Error: 9-10 expected named pair, found expression // Error: 9-10 expected named pair, found expression