Skip to content

Commit e5920c2

Browse files
RiskRunner0meta-codesync[bot]
authored andcommitted
Reject unparenthesized trailing-comma tuples in the LALRPOP parser
Summary: Make the current LALRPOP parser reject spec-invalid unparenthesized tuple forms with trailing commas, while still allowing parenthesized tuples such as `(1,)`. This follows the Starlark language spec: https://starlark-lang.org/spec.html which says that "Starlark, unlike Python, does not permit a trailing comma to appear in an unparenthesized tuple expression". It also aligns with the upstream starlark-go parser tests in: https://github.com/google/starlark-go/blob/master/syntax/testdata/errors.star which cover `_ = 1,`, `a, b, = 1, 2`, and `a, b = 1, 2,`. Add matching regressions for the current parser, including `for` and comprehension target cases that exercise the same rule through assignment targets. Update the unmatched-parens golden to reflect the broader `")" or ","` expectation introduced by the grammar split. An Eden-backed exhaustive `BUCK`/`TARGETS` parse sweep found one real in-repo user of the old bug in `fbcode/third-party-source/java/com.google.tsunami/BUCK`, where a commented-out rule leaves `test_type = "junit",` as a top-level assignment. That follow-up is tracked separately in D100062221. Reviewed By: cjhopman Differential Revision: D100064428 fbshipit-source-id: 3d64085fe6e8b9e6cdff3c28f088bf8341886339
1 parent d459870 commit e5920c2

6 files changed

Lines changed: 111 additions & 7 deletions

File tree

starlark_syntax/src/syntax/grammar.lalrpop

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ LoadStmtSyms: (AstAssignIdent, AstString) = <id:LoadStmtBindingName?> <n:string>
221221
};
222222

223223
// Expression
224-
L<E>: AstExpr = <l:@L> <v:(<E> ",")*> <e:E> <f:","?> <r:@R>
224+
ParenTuple<E>: AstExpr = <l:@L> <v:(<E> ",")*> <e:E> <f:","?> <r:@R>
225225
=> {
226226
if f.is_some() || !v.is_empty() {
227227
Expr::Tuple(v.into_iter().chain(vec![e].into_iter()).collect())
@@ -231,9 +231,19 @@ L<E>: AstExpr = <l:@L> <v:(<E> ",")*> <e:E> <f:","?> <r:@R>
231231
}
232232
};
233233

234-
ExprList: AstExpr = L<Expr>;
234+
UnparenTuple<E>: AstExpr = {
235+
<l:@L> <e:E> "," <r:@R>
236+
=>? Ok(grammar_util::reject_unparenthesized_tuple_trailing_comma(state.codemap, l, r)?),
237+
<l:@L> <v:(<E> ",")+> <e:E> "," <r:@R>
238+
=>? Ok(grammar_util::reject_unparenthesized_tuple_trailing_comma(state.codemap, l, r)?),
239+
<l:@L> <v:(<E> ",")+> <e:E> <r:@R>
240+
=> Expr::Tuple(v.into_iter().chain(vec![e].into_iter()).collect()).ast(l, r),
241+
E,
242+
};
243+
244+
ExprList: AstExpr = UnparenTuple<Expr>;
235245

236-
TestList: AstExpr = L<Test>;
246+
TestList: AstExpr = UnparenTuple<Test>;
237247

238248

239249
PrimaryExpr: AstExpr = {
@@ -282,7 +292,7 @@ Operand: AstExpr = {
282292
<l:@L> "{" <e:COMMA<DictEntry>> "}" <r:@R>
283293
=> Expr::Dict(e).ast(l, r),
284294
DictComp,
285-
<l:@L> "(" <e:TestList?> ")" <r:@R>
295+
<l:@L> "(" <e:ParenTuple<Test>?> ")" <r:@R>
286296
=> match e {
287297
Some(t) => t,
288298
None => Expr::Tuple(vec![]).ast(l, r)

starlark_syntax/src/syntax/grammar_tests.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,21 @@ fn test_if_elif_span_covers_full_chain() {
752752
"if True:\n a\nelif False:\n b\nelse:\n c",
753753
);
754754
}
755+
756+
#[test]
757+
fn test_error_tuple_trailing_comma() {
758+
parse_fails(
759+
"error_tuple_trailing_comma",
760+
&[
761+
"_ = 1,",
762+
"for k, v, in dict.items():\n pass",
763+
"_ = [(v, k) for k, v, in dict.items()]",
764+
"a, b, = 1, 2",
765+
"a, b = 1, 2,",
766+
],
767+
);
768+
}
769+
755770
pub fn parse(program: &str) -> String {
756771
parse_ast(program).statement.to_string()
757772
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# @generated
2+
# To regenerate, run:
3+
# ```
4+
# STARLARK_RUST_REGENERATE_GOLDEN_TESTS=1 cargo test -p starlark --lib
5+
# ```
6+
7+
Program:
8+
_ = 1,
9+
10+
Error:
11+
error: unparenthesized tuple with trailing comma
12+
--> error_tuple_trailing_comma:1:5
13+
|
14+
1 | _ = 1,
15+
| ^^
16+
|
17+
18+
19+
Program:
20+
for k, v, in dict.items():
21+
pass
22+
23+
Error:
24+
error: unparenthesized tuple with trailing comma
25+
--> error_tuple_trailing_comma:1:5
26+
|
27+
1 | for k, v, in dict.items():
28+
| ^^^^^
29+
|
30+
31+
32+
Program:
33+
_ = [(v, k) for k, v, in dict.items()]
34+
35+
Error:
36+
error: unparenthesized tuple with trailing comma
37+
--> error_tuple_trailing_comma:1:17
38+
|
39+
1 | _ = [(v, k) for k, v, in dict.items()]
40+
| ^^^^^
41+
|
42+
43+
44+
Program:
45+
a, b, = 1, 2
46+
47+
Error:
48+
error: unparenthesized tuple with trailing comma
49+
--> error_tuple_trailing_comma:1:1
50+
|
51+
1 | a, b, = 1, 2
52+
| ^^^^^
53+
|
54+
55+
56+
Program:
57+
a, b = 1, 2,
58+
59+
Error:
60+
error: unparenthesized tuple with trailing comma
61+
--> error_tuple_trailing_comma:1:8
62+
|
63+
1 | a, b = 1, 2,
64+
| ^^^^^
65+
|

starlark_syntax/src/syntax/grammar_tests/error_unexpected_token.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Program:
129129
a = max(range(10)))
130130

131131
Error:
132-
error: Parse error: unexpected symbol ')' here, expected one of "\n" or ";"
132+
error: Parse error: unexpected symbol ')' here, expected one of "\n", "," or ";"
133133
--> error_unexpected_token:1:19
134134
|
135135
1 | a = max(range(10)))

starlark_syntax/src/syntax/grammar_tests/error_unmatched_parens.golden

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Program:
88
(1 + 2
99

1010
Error:
11-
error: Parse error: unexpected new line here, expected one of ")"
11+
error: Parse error: unexpected new line here, expected one of ")" or ","
1212
--> error_unmatched_parens:1:7
1313
|
1414
1 | (1 + 2
@@ -58,7 +58,7 @@ x = (1 +
5858
3
5959

6060
Error:
61-
error: Parse error: unexpected new line here, expected one of ")"
61+
error: Parse error: unexpected new line here, expected one of ")" or ","
6262
--> error_unmatched_parens:3:4
6363
|
6464
3 | 3

starlark_syntax/src/syntax/grammar_util.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ enum GrammarUtilError {
6868
TypeAnnotationOnTupleAssign,
6969
#[error("`load` statement requires at least two arguments")]
7070
LoadRequiresAtLeastTwoArguments,
71+
#[error("unparenthesized tuple with trailing comma")]
72+
UnparenthesizedTupleTrailingComma,
7173
}
7274

7375
/// Ensure we produce normalised Statements, rather than singleton Statements
@@ -146,6 +148,18 @@ pub fn check_assignment(
146148
})
147149
}
148150

151+
pub(crate) fn reject_unparenthesized_tuple_trailing_comma<T>(
152+
codemap: &CodeMap,
153+
begin: usize,
154+
end: usize,
155+
) -> Result<T, EvalException> {
156+
Err(EvalException::new_anyhow(
157+
GrammarUtilError::UnparenthesizedTupleTrailingComma.into(),
158+
Span::new(Pos::new(begin as _), Pos::new(end as _)),
159+
codemap,
160+
))
161+
}
162+
149163
pub(crate) fn check_load_0(module: AstString, parser_state: &mut ParserState) -> Stmt {
150164
parser_state.errors.push(EvalException::new_anyhow(
151165
GrammarUtilError::LoadRequiresAtLeastTwoArguments.into(),

0 commit comments

Comments
 (0)