Skip to content

Commit

Permalink
Mark duplicated arguments in argument-patterns as syntax error
Browse files Browse the repository at this point in the history
To remain consistent with `nix-instantiate --parser`, expressions like

    { a, a }: a

should be marked as invalid.

Closes #45
  • Loading branch information
Ma27 committed Nov 11, 2021
1 parent 65fbe7a commit 850e358
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 2 deletions.
36 changes: 34 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! The parser: turns a series of tokens into an AST
use std::{
collections::{HashSet, VecDeque},
collections::{HashSet, HashMap, VecDeque},
fmt,
};

Expand Down Expand Up @@ -34,6 +34,8 @@ pub enum ParseError {
UnexpectedEOF,
/// UnexpectedEOFWanted is used when specific tokens are expected, but the end of file is reached
UnexpectedEOFWanted(Box<[SyntaxKind]>),
/// UnexpectedDuplicatedArgs is used when formal arguments are duplicated, e.g. `{ a, a }`
UnexpectedDuplicatedArgs(TextRange, String),
}

impl fmt::Display for ParseError {
Expand Down Expand Up @@ -75,6 +77,15 @@ impl fmt::Display for ParseError {
ParseError::UnexpectedEOFWanted(kinds) => {
write!(f, "unexpected end of file, wanted any of {:?}", kinds)
}
ParseError::UnexpectedDuplicatedArgs(range, ident) => {
write!(
f,
"argument `{}' is duplicated in {}..{}",
ident,
usize::from(range.start()),
usize::from(range.end())
)
}
}
}
}
Expand Down Expand Up @@ -326,6 +337,7 @@ where
if self.peek().map(|t| t == TOKEN_CURLY_B_CLOSE).unwrap_or(true) {
self.bump();
} else {
let mut args = HashMap::<SmolStr, TextSize>::new();
loop {
match self.expect_peek_any(&[TOKEN_CURLY_B_CLOSE, TOKEN_ELLIPSIS, TOKEN_IDENT]) {
Some(TOKEN_CURLY_B_CLOSE) => {
Expand All @@ -339,8 +351,28 @@ where
}
Some(TOKEN_IDENT) => {
self.start_node(NODE_PAT_ENTRY);
let tp = self.get_text_position();
let mut ident_done = false;
if let Some((_, ident_name)) = self.peek_raw() {
let contains = args.contains_key(ident_name);
let id = ident_name.clone();
let id_str = ident_name.to_string().clone();
args.insert(id, tp);
if contains {
ident_done = true;
self.start_error_node();
self.expect_ident();
let fin = self.finish_error_node();
self.errors.push(ParseError::UnexpectedDuplicatedArgs(
TextRange::new(tp, fin),
id_str
));
}
}

self.expect_ident();
if !ident_done {
self.expect_ident();
}

if let Some(TOKEN_QUESTION) = self.peek() {
self.bump();
Expand Down
38 changes: 38 additions & 0 deletions test_data/parser/patterns/8.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
error: argument `a' is duplicated in 8..9
error: error node at 8..9
NODE_ROOT 0..14 {
NODE_LAMBDA 0..14 {
NODE_PATTERN 0..11 {
TOKEN_CURLY_B_OPEN("{") 0..1
TOKEN_WHITESPACE(" ") 1..2
NODE_PAT_ENTRY 2..3 {
NODE_IDENT 2..3 {
TOKEN_IDENT("a") 2..3
}
}
TOKEN_COMMA(",") 3..4
TOKEN_WHITESPACE(" ") 4..5
NODE_PAT_ENTRY 5..6 {
NODE_IDENT 5..6 {
TOKEN_IDENT("b") 5..6
}
}
TOKEN_COMMA(",") 6..7
TOKEN_WHITESPACE(" ") 7..8
NODE_PAT_ENTRY 8..9 {
NODE_ERROR 8..9 {
NODE_IDENT 8..9 {
TOKEN_IDENT("a") 8..9
}
}
}
TOKEN_WHITESPACE(" ") 9..10
TOKEN_CURLY_B_CLOSE("}") 10..11
}
TOKEN_COLON(":") 11..12
TOKEN_WHITESPACE(" ") 12..13
NODE_IDENT 13..14 {
TOKEN_IDENT("a") 13..14
}
}
}
1 change: 1 addition & 0 deletions test_data/parser/patterns/8.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ a, b, a }: a

0 comments on commit 850e358

Please # to comment.